import { resolve } from 'path'
import Router from 'koa-router'
import glob from 'glob'
import _ from 'lodash'
import R from 'ramda'

export const symbolPrefix = Symbol('prefix')
export let routersMap = new Map()

export const isArray = c => _.isArray(c) ? c : [c]

export const normalizePath = path => path.startsWith('/') ? path : `/${path}`

export class Route {
  constructor (app, apiPath) {
    this.app = app
    this.router = new Router()
    this.apiPath = apiPath
  }

  // 将装饰器上的路径与方法匹配
  // 1.将controler,get,post,put,delete装饰器中的绝对路径和相对路径组合成绝对路由路径
  // 2.将绝对路由路径和装饰器传入的方法传给koa-router
  init () {
    // 读取apiPath路径下的所有js文件,并引入js文件
    // 在引入js文件时，系统运行装饰器函数controler,get,post,put,delete，初始化全局变量routersMap
    // glob.sync：遍历js文件数组
    // forEach(require):引入js文件，在引入文件时，系统执行注解函数
    glob.sync(resolve(this.apiPath, './*.js')).forEach(require)
    _.forIn(routersMap, (value, key) => {
      console.log(value, key)
    })

    for (let [ conf, controller ] of routersMap) {
      const controllers = isArray(controller)
      // 取出路由前缀路径
      let prefixPath = conf.target[symbolPrefix]
      if (prefixPath) prefixPath = normalizePath(prefixPath)
      // 将路由前缀路径和路由相对路径拼装成路由绝对路径
      const routerPath = prefixPath + conf.path

      // 例如：
      // this.router.get('/wechat/hello', async ctx => {
      //   ctx.body = 'Hello World'
      // })
      this.router[conf.method](routerPath, ...controllers)
    }

    this.app.use(this.router.routes())
    this.app.use(this.router.allowedMethods())
  }
}

// 将装饰器get,post,delete,put中的相对路径装饰器拼装到routerMap中
export const router = conf => (target, key, descriptor) => {
  conf.path = normalizePath(conf.path)

  routersMap.set(
    {
      target: target,    // 装饰器注解的对象
      ...conf            // conf.method: http的method:get,post,put,delete
                         // conf.path: 路由的相对路径
    }, 
    target[key]          // 装饰器注解的函数，key:装饰器注解的函数名
  )
}

// 装饰器controller记录路径前缀
export const controller = path => target => target.prototype[symbolPrefix] = path

// 注解函数在js文件require时调用
// 将装饰器get,post,delete,put中的相对路径装饰器拼装到routerMap中
export const get = path => router({
  method: 'get',
  path: path
})

export const post = path => router({
  method: 'post',
  path: path
})

export const put = path => router({
  method: 'put',
  path: path
})

export const del = path => router({
  method: 'delete',
  path: path
})


const decorate = (args, middleware) => {
  let [ target, key, descriptor ] = args

  target[key] = isArray(target[key])
  target[key].unshift(middleware)

  return descriptor
}

export const convert = middleware => (...args) => decorate(args, middleware)


/*
  @required({
    body: ['username', 'password'],
    query: ['token']
  })
*/
export const required = rules => convert(async (ctx, next) => {
  console.log('进入required')
  let errors = []
  const passRules = R.forEachObjIndexed(
    (value, key) => {
      errors = R.filter(i => !R.has(i, ctx.request[key]))(value)
    }
  )
  passRules(rules)

  if (errors.length) ctx.throw(412, `${errors.join(',')} is required`)
  await next()
})

// export const log = convert(async (ctx, next) => {
//   let currentReqID = reqID++
//   console.time(`${currentReqID} ${ctx.method} ${ctx.url}`)
//   await next()
//   console.timeEnd(`${currentReqID} ${ctx.method} ${ctx.url}`)
// })